package com.skivingcloud.report.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.skivingcloud.common.utils.GenericClass;
import com.skivingcloud.common.utils.JacksonUtil;
import com.skivingcloud.report.config.DataSourceConfig;
import com.skivingcloud.report.config.InfluxDbDataSourceConfig;
import com.skivingcloud.report.constants.RedisPrefixEnum;
import com.skivingcloud.report.constants.ReportConstant;
import com.skivingcloud.report.entity.MesGetDynamicData;
import com.skivingcloud.report.entity.ReportDatasource;
import com.skivingcloud.report.entity.ReportTplDataset;
import com.skivingcloud.report.mapper.ReportTplDatasetMapper;
import com.skivingcloud.report.service.JdbcService;
import com.skivingcloud.report.service.ReportDatasourceService;
import com.skivingcloud.report.service.ReportTplDatasetService;
import com.skivingcloud.report.utils.InfluxDBConnection;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.skivingcloud.common.utils.PageParameter;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.*;

/**
 *  服务层实现。
 *
 * @author skivingcloud
 * @since 2024-11-07
 */
@Service
public class ReportTplDatasetServiceImpl extends ServiceImpl<ReportTplDatasetMapper, ReportTplDataset> implements ReportTplDatasetService {

    private static final Logger log = LoggerFactory.getLogger(ReportTplDatasetServiceImpl.class);

    private final ReportDatasourceService reportDatasourceService;
    private final RedisTemplate<Object, Object> redisTemplate;
    private final JdbcService jdbcService;
    private final RestTemplate restTemplateOuter;

    public ReportTplDatasetServiceImpl(ReportDatasourceService reportDatasourceService, RedisTemplate<Object, Object> redisTemplate, JdbcService jdbcService, @Qualifier("restTemplateOuter") RestTemplate restTemplateOuter) {
        this.reportDatasourceService = reportDatasourceService;
        this.redisTemplate = redisTemplate;
        this.jdbcService = jdbcService;
        this.restTemplateOuter = restTemplateOuter;
    }

    /**
     * 分页查询
     *
     * @param parameter 查询参数
     * @return 分页查询结果
     */
    @Override
    public Page<ReportTplDataset> queryByPage(PageParameter<ReportTplDataset> parameter){
        Page<ReportTplDataset> page = new Page<>(parameter.getPage(), parameter.getPageSize());
        QueryWrapper queryWrapper = QueryWrapper.create();
        if (parameter.getParam() != null) {
            //查询条件
            if (StringUtils.isNoneBlank(parameter.getParam().getId())) {
                queryWrapper.and(ReportTplDataset::getId).like(parameter.getParam().getId());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getTenantId())) {
                queryWrapper.and(ReportTplDataset::getTenantId).like(parameter.getParam().getTenantId());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getDatasetType())) {
                queryWrapper.and(ReportTplDataset::getDatasetType).like(parameter.getParam().getDatasetType());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getDatasetName())) {
                queryWrapper.and(ReportTplDataset::getDatasetName).like(parameter.getParam().getDatasetName());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getTplId())) {
                queryWrapper.and(ReportTplDataset::getTplId).like(parameter.getParam().getTplId());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getDatasourceId())) {
                queryWrapper.and(ReportTplDataset::getDatasourceId).like(parameter.getParam().getDatasourceId());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getTplSql())) {
                queryWrapper.and(ReportTplDataset::getTplSql).like(parameter.getParam().getTplSql());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getTplParam())) {
                queryWrapper.and(ReportTplDataset::getTplParam).like(parameter.getParam().getTplParam());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getSqlType())) {
                queryWrapper.and(ReportTplDataset::getSqlType).like(parameter.getParam().getSqlType());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getInParam())) {
                queryWrapper.and(ReportTplDataset::getInParam).like(parameter.getParam().getInParam());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getOutParam())) {
                queryWrapper.and(ReportTplDataset::getOutParam).like(parameter.getParam().getOutParam());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getIsPagination())) {
                queryWrapper.and(ReportTplDataset::getIsPagination).like(parameter.getParam().getIsPagination());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getType())) {
                queryWrapper.and(ReportTplDataset::getType).like(parameter.getParam().getType());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getCreator())) {
                queryWrapper.and(ReportTplDataset::getCreator).like(parameter.getParam().getCreator());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getUpdater())) {
                queryWrapper.and(ReportTplDataset::getUpdater).like(parameter.getParam().getUpdater());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getDelFlag())) {
                queryWrapper.and(ReportTplDataset::getDelFlag).like(parameter.getParam().getDelFlag());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getCurrentPageAttr())) {
                queryWrapper.and(ReportTplDataset::getCurrentPageAttr).like(parameter.getParam().getCurrentPageAttr());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getPageCountAttr())) {
                queryWrapper.and(ReportTplDataset::getPageCountAttr).like(parameter.getParam().getPageCountAttr());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getTotalAttr())) {
                queryWrapper.and(ReportTplDataset::getTotalAttr).like(parameter.getParam().getTotalAttr());
            }
        }
        return this.page(page, queryWrapper);
    }

    /**
     * 根据模板主键获取数据集
     *
     * @param id 模板主键
     * @return 数据集
     */
    @Override
    public List<ReportTplDataset> getTplDatasets(Serializable id) {
        QueryWrapper queryWrapper = QueryWrapper.create().and(ReportTplDataset::getTplId).eq(id)
                .and(ReportTplDataset::getDelFlag).eq(ReportConstant.COMMON_YES_OR_NO_YES);
        return this.mapper.selectListByQuery(queryWrapper);
    }

    /**
     * 根据模板主键获取数据集
     *
     * @param id 数据集主键
     * @return 列
     */
    @Override
    public List<Map<String, Object>> getDataSetColumns(Serializable id) throws SQLException, JsonProcessingException {
        ReportTplDataset dataset = this.getById(id);
        Assert.notNull(dataset, "数据集不存在");
        ReportDatasource reportDatasource = reportDatasourceService.getById(dataset.getDatasourceId());
        Assert.notNull(reportDatasource, "数据源不存在");
        List<Map<String, Object>> cacheColumns = null;
        if(ReportConstant.DatasetTypeEnum.SQL.getCode().equals(dataset.getDatasetType())){
            cacheColumns =(List<Map<String, Object>>) redisTemplate.opsForValue().get(RedisPrefixEnum.DATASET_COLUMN.getCode() + dataset.getId());
        }else {//api
            cacheColumns =(List<Map<String, Object>>) redisTemplate.opsForValue().get(RedisPrefixEnum.DATASET_COLUMN.getCode() + reportDatasource.getId());
        }
        if (cacheColumns != null) {
            return cacheColumns;
        }
        List<Map<String, Object>> columns = new ArrayList<>();
        if(ReportConstant.DatasetTypeEnum.SQL.getCode().equals(dataset.getDatasetType())){
            switch (reportDatasource.getType()) {
                case ReportConstant.INFLUXDB -> {
                    InfluxDbDataSourceConfig influxConfig = new InfluxDbDataSourceConfig(reportDatasource.getId(),
                            reportDatasource.getUserName(),
                            reportDatasource.getPassword(),
                            reportDatasource.getJdbcUrl().substring(0,reportDatasource.getJdbcUrl().lastIndexOf("/")),
                            reportDatasource.getJdbcUrl().substring(reportDatasource.getJdbcUrl().lastIndexOf("/")+1));
                    InfluxDBConnection dbConnection = jdbcService.getInfluxdbDatasource(influxConfig);
                    Assert.notNull(dbConnection, "influxdb连接失败");
                    columns = jdbcService.parseInfluxdbColumns(dbConnection, dataset.getTplSql(), reportDatasource.getType(), dataset.getTplParam());
                }
                case ReportConstant.ELASTIC_SEARCH_SQL,
                     ReportConstant.MYSQL,
                     ReportConstant.KUDU_IMAPLA,
                     ReportConstant.ORACLE,
                     ReportConstant.SQL_SERVER,
                     ReportConstant.JDBC,
                     ReportConstant.POSTGRESQL,
                     ReportConstant.DAMENG,
                     ReportConstant.OPENGAUSS,
                     ReportConstant.KINGBASE,
                     ReportConstant.TDENGINE -> {
                        DataSourceConfig config = new DataSourceConfig(dataset.getDatasourceId(), reportDatasource.getType(),
                                reportDatasource.getJdbcUrl(), reportDatasource.getUserName(), reportDatasource.getPassword(),
                                null);
                        columns = jdbcService.parseMetaDataColumns(config, dataset.getTplSql(),reportDatasource.getType(),dataset.getTplParam());
                    }
            }
        }else{
            List<Map<String, Object>> jsonArray = JacksonUtil.jsonToListMap(reportDatasource.getApiColumns());
            columns = jsonArray.stream().map(m -> {
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("columnName", m.get("propName"));
                map.put("name", m.get("propCode"));
                return map;
            }).toList();
        }
        redisTemplate.opsForValue().set(RedisPrefixEnum.DATASET_COLUMN.getCode() + dataset.getId(), columns);
        return columns;
    }

    /**
     * 获取动态数据
     *
     * @param mesGetDynamicData 参数
     * @return 动态数据
     */
    @Override
    public List<Map<String, Object>> getDynamicDatas(MesGetDynamicData mesGetDynamicData) throws SQLException, JsonProcessingException {
        List<Map<String, Object>> result = new ArrayList<>();
        if(mesGetDynamicData.getDataSetId() != null && !mesGetDynamicData.getDataColumns().isEmpty()) {
            ReportTplDataset dataset = this.mapper.selectOneById(mesGetDynamicData.getDataSetId());
            ReportDatasource datasource = reportDatasourceService.getById(dataset.getDatasourceId());
            if(ReportConstant.DatasetTypeEnum.SQL.getCode().equals(dataset.getDatasetType())){
                String sql = jdbcService.processSqlParams(dataset.getTplSql(), mesGetDynamicData.getParams());
                switch (datasource.getType()) {
                    case ReportConstant.INFLUXDB -> {
                        InfluxDbDataSourceConfig influxConfig = new InfluxDbDataSourceConfig(datasource.getId(),
                                datasource.getUserName(),
                                datasource.getPassword(),
                                datasource.getJdbcUrl().substring(0,datasource.getJdbcUrl().lastIndexOf("/")),
                                datasource.getJdbcUrl().substring(datasource.getJdbcUrl().lastIndexOf("/")+1));
                        InfluxDBConnection dbConnection = jdbcService.getInfluxdbDatasource(influxConfig);
                        Assert.notNull(dbConnection, "influxdb连接失败");
                        result = jdbcService.getInfluxdbData(dbConnection, sql);
                    }
                    case ReportConstant.ELASTIC_SEARCH_SQL,
                         ReportConstant.MYSQL,
                         ReportConstant.KUDU_IMAPLA,
                         ReportConstant.ORACLE,
                         ReportConstant.SQL_SERVER,
                         ReportConstant.JDBC,
                         ReportConstant.POSTGRESQL,
                         ReportConstant.DAMENG,
                         ReportConstant.OPENGAUSS,
                         ReportConstant.KINGBASE,
                         ReportConstant.TDENGINE -> {
                        DataSourceConfig config = new DataSourceConfig(dataset.getDatasourceId(), datasource.getType(),
                                datasource.getJdbcUrl(), datasource.getUserName(), datasource.getPassword(),
                                null);
                        result = jdbcService.getDatasourceDataBySql(config, sql);
                    }
                }
            }else{
                Map<String, String> headerMap = new HashMap<>();
                if(StringUtils.isNotEmpty(datasource.getApiRequestHeader())){
                    headerMap = JacksonUtil.jsonStringToMap(datasource.getApiRequestHeader());
                }
                HttpHeaders headers = new HttpHeaders();
                headers.setAll(headerMap);
                HttpEntity<Map<String, Object>> entity = new HttpEntity<>(mesGetDynamicData.getParams(), headers);
                ResponseEntity<Map<String, Object>> exchange = restTemplateOuter.exchange(datasource.getJdbcUrl(),
                        HttpMethod.valueOf(datasource.getApiRequestType()),
                        entity, new GenericClass<Map<String, Object>>().getType());
                if (exchange.getStatusCode().isError()) {
                    log.error("测试连接失败{}", Objects.requireNonNull(exchange.getBody()));
                }else{
                    result = (List<Map<String, Object>>) exchange.getBody().get("datas");
                }
            }
        }
        return result;
    }
}
